package demo.dcs.aspect;

import demo.dcs.MertricListener;
import demo.dcs.MertricSendSchedule;
import demo.dcs.util.XdcsBeanUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

import java.lang.reflect.Field;


public class SpringRedisPoolAspectJ extends AbstractXdcsAspectJ{

	private static final Logger logger = LoggerFactory.getLogger(SpringRedisPoolAspectJ.class);

    @Override
	public void beforeAspectJ(Object signature, Object instance, Object[] args) {
	}

	@Override
	public void afterAspectJ(Object result, Object signature, Object instance, Object[] args, Throwable throwable) {
		try {
			if (!mercurySwitch()) {
				return;
			}
			if (instance instanceof JedisConnectionFactory) {
                JedisConnectionFactory jedisConnectionFactory = (JedisConnectionFactory)instance;
				StringBuilder redisIdBuilder = new StringBuilder();
                if (StringUtils.isNotBlank(jedisConnectionFactory.getHostName())) {
                    redisIdBuilder.append(jedisConnectionFactory.getHostName()).append(":").append(jedisConnectionFactory.getPort());
                    redisIdBuilder.append("-").append(jedisConnectionFactory.getDatabase());
                }
				String hostString = redisIdBuilder.toString();
                try {
                    Field poolField = JedisConnectionFactoryFieldCache.getInstance().getPoolFiled();
                    Object pool = poolField.get(instance);
                    Field field = JedisPoolFieldsCache.getInstance().getInternalPoolFiled();
                    if(pool == null || field == null){
                        return;
                    }
                    Object internalPool = field.get(pool);
                    MertricListener mertricListener = null;
                    if (XdcsBeanUtils.isClassExist("org.apache.commons.pool.impl.GenericObjectPool") && internalPool instanceof org.apache.commons.pool.impl.GenericObjectPool){
                        mertricListener = new GenericObjectPoolListener((org.apache.commons.pool.impl.GenericObjectPool)internalPool, hostString);
                    }
                    if (XdcsBeanUtils.isClassExist("org.apache.commons.pool2.impl.GenericObjectPool") && internalPool instanceof org.apache.commons.pool2.impl.GenericObjectPool){
                        mertricListener = new GenericObjectPool2Listener((org.apache.commons.pool2.impl.GenericObjectPool)internalPool, hostString);
                    }
                    if (mertricListener!=null){
                        MertricSendSchedule.getMertricSendSchedule().addMertricListener(mertricListener);
                    }
                }catch (Throwable e){
                    logger.error("SpringDataRedisAspectJ afterAspectJ get genericObjectPool error", e);
                }
			}
		} catch (Throwable e) {
			logger.error("SpringDataRedisAspectJ afterAspectJ", e);
		}
	}


    private static class JedisConnectionFactoryFieldCache {

        private static JedisConnectionFactoryFieldCache instance;
        private final Field poolFiled;

        private JedisConnectionFactoryFieldCache() throws Exception {
            poolFiled = JedisConnectionFactory.class.getDeclaredField("pool");
            poolFiled.setAccessible(true);
        }

        public static JedisConnectionFactoryFieldCache getInstance()throws Exception {

            if (instance == null) {
                synchronized (JedisConnectionFactoryFieldCache.class) {
                    if (instance == null) {
                        instance = new JedisConnectionFactoryFieldCache();
                    }
                }
            }
            return instance;
        }

        public Field getPoolFiled() {
            return poolFiled;
        }
    }

}
